package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.hmall.common.Constant.SearchConstant;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.ItemDOC;
import com.hmall.search.pojo.ItemSearch;
import com.hmall.search.service.SearchService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    RestHighLevelClient client;
    
    /**
     * 搜索自动补全
     *
     * @param key
     * @return
     */
    @Override
    public List<String> search(String key) {
        if (key == null || "".equals(key)) {
            return null;
        }
        //设置搜索自动补全
        SearchRequest searchRequest = new SearchRequest(SearchConstant.INDEX_NAME);
        searchRequest.source().suggest(new SuggestBuilder().addSuggestion(SearchConstant.SUGGEST_NAME,
                SuggestBuilders.completionSuggestion(SearchConstant.SUGGEST_FIELD_NAME)
                        .prefix(key).skipDuplicates(true).size(10)));
        //发送请求
        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            //处理结果
            Suggest suggest = searchResponse.getSuggest();
            //根据名称获取补全结果
            CompletionSuggestion suggestion = suggest.getSuggestion(SearchConstant.SUGGEST_NAME);
            //将结果转换为String类型
            return suggestion.getOptions().stream().map(s -> s.getText().string()).collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 搜索分页查询
     *
     * @return
     */
    @Override
    public PageDTO<ItemDOC> list(ItemSearch itemSearch) {
        //创建搜索对象
        SearchRequest searchRequest = new SearchRequest(SearchConstant.INDEX_NAME);
        //获取布尔查询结果
        BoolQueryBuilder boolQueryBuilder = boolQuery(itemSearch);
        //搜索分页
        searchRequest.source().from((itemSearch.getPage() - 1) * itemSearch.getSize()).size(itemSearch.getSize());
        //添加权重排序为首位
        searchRequest.source().sort("_score", SortOrder.DESC);
        //搜索排序
        if ("default".equals(itemSearch.getSortBy()) || "price".equals(itemSearch.getSortBy())) {
            searchRequest.source().sort("price", SortOrder.ASC);
        } else {
            searchRequest.source().sort("sold", SortOrder.DESC);
        }
        //为广告位添加权重
        FunctionScoreQueryBuilder isAD = QueryBuilders.functionScoreQuery(boolQueryBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("isAD", true),
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )
        });
        searchRequest.source().query(isAD);
        //发送请求
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            //处理结果并返回
            return parseQuery(searchResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 根据过滤条件聚合查询
     * @param itemSearch
     * @return
     */
    @Override
    public Map<String, List<String>> filter(ItemSearch itemSearch) {
        //创建搜索对象
        SearchRequest searchRequest = new SearchRequest(SearchConstant.INDEX_NAME);
        //不查询文档
        searchRequest.source().size(0);
        //获取布尔查询结果
        BoolQueryBuilder boolQueryBuilder = boolQuery(itemSearch);
        searchRequest.source().query(boolQueryBuilder);
        //聚合查询分类
        searchRequest.source().aggregation(AggregationBuilders.terms(SearchConstant.POLYMERIZATION_CATEGORY)
                .field(SearchConstant.QUERY_CATEGORY).size(10));
        //聚合查询品牌
        searchRequest.source().aggregation(AggregationBuilders.terms(SearchConstant.POLYMERIZATION_BRAND)
                .field(SearchConstant.QUERY_BRAND).size(10));
        //发送请求
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            //解析结果
            Aggregations aggregations = searchResponse.getAggregations();
            //获取分类结果
            List<String> categoryBucketList = parseBuckets(aggregations,SearchConstant.POLYMERIZATION_CATEGORY);
            //获取品牌结果
            List<String> brandBucketList = parseBuckets(aggregations,SearchConstant.POLYMERIZATION_BRAND);
            //创建返回结果的map集合并赋值
            Map<String, List<String>> result = new HashMap<>();
            result.put(SearchConstant.QUERY_CATEGORY, categoryBucketList);
            result.put(SearchConstant.QUERY_BRAND, brandBucketList);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 解析桶聚合结果
     * @param aggregations
     * @param aggName
     * @return
     */
    private List parseBuckets(Aggregations aggregations,String aggName){
        //根据名称获取聚合集
        Terms Aggs = aggregations.get(aggName);
        //获取聚合集内的桶聚合结果
        List<? extends Terms.Bucket> cityAggsBuckets = Aggs.getBuckets();
        //使用stream流将结果放入list集合内
        List<String> AggsList = cityAggsBuckets.stream().map(bucket -> bucket.getKeyAsString()).collect(Collectors.toList());
        return AggsList;
    }
    
    /**
     * 搜索结果解析
     *
     * @param searchResponse
     * @return
     */
    public PageDTO<ItemDOC> parseQuery(SearchResponse searchResponse) {
        PageDTO<ItemDOC> pageDTO = new PageDTO<>();
        List<ItemDOC> itemDOCS = new ArrayList<>();
        //获取总条数
        SearchHits hits = searchResponse.getHits();
        pageDTO.setTotal(hits.getTotalHits().value);
        //获取查询结果数组
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String json = hit.getSourceAsString();
            //使用DOC来接收数据
            ItemDOC itemDOC = JSONObject.parseObject(json, ItemDOC.class);
            //添加DOC至DOC集合
            itemDOCS.add(itemDOC);
        }
        pageDTO.setList(itemDOCS);
        return pageDTO;
    }
    
    /**
     * 根据条件进行布尔查询
     *
     * @param itemSearch
     * @return
     */
    public BoolQueryBuilder boolQuery(ItemSearch itemSearch) {
        //创建布尔查询对象
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //判断关键词是否为空
        if (StringUtils.isEmpty(itemSearch.getKey())) {
            //为空查询全部数据
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            //不为空则根据关键词查询
            boolQueryBuilder.must(QueryBuilders.matchQuery(SearchConstant.QUERY, itemSearch.getKey()));
        }
        //判断品牌
        if (StringUtils.isNotEmpty(itemSearch.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery(SearchConstant.QUERY_BRAND, itemSearch.getBrand()));
        }
        //判断类别
        if (StringUtils.isNotEmpty(itemSearch.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery(SearchConstant.QUERY_CATEGORY, itemSearch.getCategory()));
        }
        //判断价格
        if (itemSearch.getMinPrice() != null && itemSearch.getMaxPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery(SearchConstant.QUERY_PRICE)
                    .gte(itemSearch.getMinPrice())
                    .lte(itemSearch.getMaxPrice()));
        }
        return boolQueryBuilder;
    }
}
